home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / commands / treecmp.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  8KB  |  316 lines

  1. /* treecmp - compare two trees        Author: Andy Tanenbaum */
  2.  
  3. /* This program recursively compares two trees and reports on differences.
  4.  * It can be used, for example, when a project consists of a large number
  5.  * of files and directories.  When a new release (i.e., a new tree) has been
  6.  * prepared, the old and new tree can be compared to give a list of what has
  7.  * changed.  The algorithm used is that the second tree is recursively
  8.  * descended and for each file or directory found, the corresponding one in
  9.  * the other tree checked.  The two arguments are not completely symmetric
  10.  * because the second tree is descended, not the first one, but reversing
  11.  * the arguments will still detect all the differences, only they will be
  12.  * printed in a different order.  The program needs lots of stack space
  13.  * because routines with local arrays are called recursively. The call is
  14.  *    treecmp [-cv] old_dir new_dir
  15.  * The -v flag (verbose) prints the directory names as they are processed.
  16.  * The -c flag (changes) just prints the names of changed and new files.
  17.  */
  18.  
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <fcntl.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include <stdio.h>
  25.  
  26. #define BUFSIZE 4096        /* size of file buffers */
  27. #define MAXPATH 128        /* longest acceptable path */
  28. #define DIRENTLEN 14        /* number of characters in a file name */
  29.  
  30. struct dirstruct {        /* layout of a directory entry */
  31.   ino_t inum;
  32.   char fname[DIRENTLEN];
  33. };
  34.  
  35. struct stat stat1, stat2;    /* stat buffers */
  36.  
  37. char buf1[BUFSIZE];        /* used for comparing bufs */
  38. char buf2[BUFSIZE];        /* used for comparing bufs */
  39.  
  40. int changes;            /* set on -c flag */
  41. int verbose;            /* set on -v flag */
  42.  
  43. main(argc, argv)
  44. int argc;
  45. char *argv[];
  46. {
  47.   char *p;
  48.  
  49.   if (argc < 3 || argc > 4) usage();
  50.   p = argv[1];
  51.   if (argc == 4) {
  52.     if (*p != '-') usage;
  53.     p++;
  54.     if (*p == '\0') usage();
  55.     while (*p) {
  56.         if (*p == 'c') changes++;
  57.         if (*p == 'v') verbose++;
  58.         if (*p != 'c' && *p != 'v') usage();
  59.         p++;
  60.     }
  61.   }
  62.   if (argc == 3)
  63.     compare(argv[1], argv[2]);
  64.   else
  65.     compare(argv[2], argv[3]);
  66.  
  67.   exit(0);
  68. }
  69.  
  70. compare(old, new)
  71. char *old, *new;
  72. {
  73. /* This is the main comparision routine.  It gets two path names as arguments
  74.  * and stats them both.  Depending on the results, it calls other routines
  75.  * to compare directories or files.
  76.  */
  77.  
  78.   int type1, type2;
  79.  
  80.   if (stat(new, &stat1) < 0) {
  81.     /* The new file does not exist. */
  82.     if (changes == 0)
  83.         fprintf(stderr, "Cannot stat: %s\n", new);
  84.     else
  85.         printf("%s\n", new);
  86.     return;
  87.   }
  88.   if (stat(old, &stat2) < 0) {
  89.     /* The old file does not exist. */
  90.     if (changes == 0) 
  91.         fprintf(stderr, "Missing file: %s\n", old);
  92.     else
  93.         printf("%s\n", new);
  94.     return;
  95.   }
  96.  
  97.   /* Examine the types of the files. */
  98.   type1 = stat1.st_mode & S_IFMT;
  99.   type2 = stat2.st_mode & S_IFMT;
  100.   if (type1 != type2) {
  101.     fprintf(stderr, "Type diff: %s and %s\n", new, old);
  102.     return;
  103.   }
  104.  
  105.   /* The types are the same. */
  106.   switch (type1) {
  107.       case S_IFREG:    regular(old, new);    break;
  108.       case S_IFDIR:    directory(old, new);    break;
  109.       case S_IFCHR:    break;
  110.       case S_IFBLK:    break;
  111.       default:        fprintf(stderr, "Unknown file type %o\n", type1);
  112.   }
  113.   return;
  114. }
  115.  
  116. regular(old, new)
  117. char *old, *new;
  118. {
  119. /* Compare to regular files.  If they are different, complain. */
  120.  
  121.   int fd1, fd2, n1, n2;
  122.   unsigned bytes;
  123.   long count;
  124.  
  125.   if (stat1.st_size != stat2.st_size) {
  126.     if (changes == 0)
  127.         printf("Size diff: %s and %s\n", new, old);
  128.     else
  129.         printf("%s\n", new);
  130.     return;
  131.   }
  132.  
  133.   /* The sizes are the same.  We actually have to read the files now. */
  134.   fd1 = open(new, O_RDONLY);
  135.   if (fd1 < 0) {
  136.     fprintf(stderr, "Cannot open %s for reading\n", new);
  137.     return;
  138.   }
  139.   fd2 = open(old, O_RDONLY);
  140.   if (fd2 < 0) {
  141.     fprintf(stderr, "Cannot open %s for reading\n", old);
  142.     return;
  143.   }
  144.   count = stat1.st_size;
  145.   while (count > 0L) {
  146.     bytes = (unsigned) (count > BUFSIZE ? BUFSIZE : count);    /* rd count */
  147.     n1 = read(fd1, buf1, bytes);
  148.     n2 = read(fd2, buf2, bytes);
  149.     if (n1 != n2) {
  150.         if (changes == 0)
  151.             printf("Length diff: %s and %s\n", new, old);
  152.         else
  153.             printf("%s\n", new);
  154.         close(fd1);
  155.         close(fd2);
  156.         return;
  157.     }
  158.  
  159.     /* Compare the buffers. */
  160.     if (memcmp((void *) buf1, (void *) buf2, (size_t) n1) != 0) {
  161.         if (changes == 0)
  162.             printf("File diff: %s and %s\n", new, old);
  163.         else
  164.             printf("%s\n", new);
  165.         close(fd1);
  166.         close(fd2);
  167.         return;
  168.     }
  169.     count -= n1;
  170.   }
  171.   close(fd1);
  172.   close(fd2);
  173. }
  174.  
  175. directory(old, new)
  176. char *old, *new;
  177. {
  178. /* Recursively compare two directories by reading them and comparing their
  179.  * contents.  The order of the entries need not be the same.
  180.  */
  181.  
  182.   int fd1, fd2, n1, n2, ent1, ent2, i, used1 = 0, used2 = 0;
  183.   char *dir1buf, *dir2buf;
  184.   char name1buf[MAXPATH], name2buf[MAXPATH];
  185.   struct dirstruct *dp1, *dp2;
  186.   unsigned dir1bytes, dir2bytes;
  187.   extern char *malloc();
  188.  
  189.   /* Allocate space to read in the directories */
  190.   dir1bytes = (unsigned) stat1.st_size;
  191.   dir1buf = malloc(dir1bytes);
  192.   if (dir1buf == 0) {
  193.     fprintf(stderr, "Cannot process directory %s: out of memory\n", new);
  194.     return;
  195.   }
  196.   dir2bytes = (unsigned) stat2.st_size;
  197.   dir2buf = malloc(dir2bytes);
  198.   if (dir2buf == 0) {
  199.     fprintf(stderr, "Cannot process directory %s: out of memory\n", old);
  200.     free(dir1buf);
  201.     return;
  202.   }
  203.  
  204.   /* Read in the directories. */
  205.   fd1 = open(new, O_RDONLY);
  206.   if (fd1 > 0) n1 = read(fd1, dir1buf, dir1bytes);
  207.   if (fd1 < 0 || n1 != dir1bytes) {
  208.     fprintf(stderr, "Cannot read directory %s\n", new);
  209.     free(dir1buf);
  210.     free(dir2buf);
  211.     if (fd1 > 0) close(fd1);
  212.     return;
  213.   }
  214.   close(fd1);
  215.  
  216.   fd2 = open(old, O_RDONLY);
  217.   if (fd2 > 0) n2 = read(fd2, dir2buf, dir2bytes);
  218.   if (fd2 < 0 || n2 != dir2bytes) {
  219.     fprintf(stderr, "Cannot read directory %s\n", old);
  220.     free(dir1buf);
  221.     free(dir2buf);
  222.     close(fd1);
  223.     if (fd2 > 0) close(fd2);
  224.     return;
  225.   }
  226.   close(fd2);
  227.  
  228.   /* Linearly search directories */
  229.   ent1 = dir1bytes / sizeof(struct dirstruct);
  230.   dp1 = (struct dirstruct *) dir1buf;
  231.   for (i = 0; i < ent1; i++) {
  232.     if (dp1->inum != 0) used1++;
  233.     dp1++;
  234.   }
  235.  
  236.   ent2 = dir2bytes / sizeof(struct dirstruct);
  237.   dp2 = (struct dirstruct *) dir2buf;
  238.   for (i = 0; i < ent2; i++) {
  239.     if (dp2->inum != 0) used2++;
  240.     dp2++;
  241.   }
  242.  
  243.   if (verbose) printf("Directory %s: %d entries\n", new, used1);
  244.  
  245.   /* Check to see if any entries in dir2 are missing from dir1. */
  246.   dp1 = (struct dirstruct *) dir1buf;
  247.   dp2 = (struct dirstruct *) dir2buf;
  248.   for (i = 0; i < ent2; i++) {
  249.     if (dp2->inum == 0 || strcmp(dp2->fname, ".") == 0 ||
  250.         strcmp(dp2->fname, "..") == 0) {
  251.         dp2++;
  252.         continue;
  253.     }
  254.     check(dp2->fname, dp1, ent1, new);
  255.     dp2++;
  256.   }
  257.  
  258.   /* Recursively process all the entries in dir1. */
  259.   dp1 = (struct dirstruct *) dir1buf;
  260.   for (i = 0; i < ent1; i++) {
  261.     if (dp1->inum == 0 || strcmp(dp1->fname, ".") == 0 ||
  262.         strcmp(dp1->fname, "..") == 0) {
  263.         dp1++;
  264.         continue;
  265.     }
  266.     if (strlen(new) + DIRENTLEN >= MAXPATH) {
  267.         fprintf(stderr, "Path too long: %s\n", new);
  268.         free(dir1buf);
  269.         free(dir2buf);
  270.         return;
  271.     }
  272.     if (strlen(old) + DIRENTLEN >= MAXPATH) {
  273.         fprintf(stderr, "Path too long: %s\n", old);
  274.         free(dir1buf);
  275.         free(dir2buf);
  276.         return;
  277.     }
  278.     strcpy(name1buf, old);
  279.     strcat(name1buf, "/");
  280.     strncat(name1buf, dp1->fname, DIRENTLEN);
  281.     strcpy(name2buf, new);
  282.     strcat(name2buf, "/");
  283.     strncat(name2buf, dp1->fname, DIRENTLEN);
  284.  
  285.     /* Here is the recursive call to process an entry. */
  286.     compare(name1buf, name2buf);    /* recursive call */
  287.     dp1++;
  288.   }
  289.  
  290.   free(dir1buf);
  291.   free(dir2buf);
  292. }
  293.  
  294. check(s, dp1, ent1, new)
  295. char *s;
  296. struct dirstruct *dp1;
  297. int ent1;
  298. char *new;
  299. {
  300. /* See if the file name 's' is present in the directory 'dirbuf'. */
  301.   int i;
  302.  
  303.   for (i = 0; i < ent1; i++) {
  304.     if (strncmp(dp1->fname, s, DIRENTLEN) == 0) return;
  305.     dp1++;
  306.   }
  307.   if (changes == 0) printf("Missing file: %s/%s\n", new, s);
  308.     
  309. }
  310.  
  311. usage()
  312. {
  313.   printf("Usage: treecmp [-cv] old_dir new_dir\n");
  314.   exit(1);
  315. }
  316.